﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;

namespace Money.Plugins
{
    // TODO we need to add checks so that there is only ever 1 plugin host in operation (singleton)
    // TODO should we update all plugins if the configruation changes
    // TODO can we make this more extensible by using reflection to get all interfaces in this assembly and then check all plugins for instances of these?
    // TODO we should load all plugins from a central location AppData?

    public class PluginHost
    {
        private static IDataSet _dataAccessInterface;
        private static List<Assembly> _plugInAssemblyList;
        private static List<IPluginGui> _guiPlugins = new List<IPluginGui>();
        private static List<IPluginImport> _importPlugins = new List<IPluginImport>();
        private static List<IGraph> _graphPlugins = new List<IGraph>();
        private static List<IPluginDataAccess> _dataAccessPlugins = new List<IPluginDataAccess>();

        public PluginHost(IDataSet dataAccessInterface)
        {
            _dataAccessInterface = dataAccessInterface;
        }

        private static List<Assembly> LoadPlugInAssemblies()
        {
            DirectoryInfo directoryInfo = new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, "."));

            // TODO when things are centralised we should try loading all dll files
            // TODO we should have a IPlugin interface that has some common items that all plugins should support and base whether we load a plugin on whether it implements this common interface
            FileInfo[] files = directoryInfo.GetFiles("Plugin-*.dll");

            _plugInAssemblyList = new List<Assembly>();

            if (null != files)
            {
                foreach (FileInfo file in files)
                {
                    _plugInAssemblyList.Add(Assembly.LoadFile(file.FullName));
                }
            }

            return _plugInAssemblyList;
        }

        public static void Load()
        {
            List<Type> interfaceList = new List<Type>();

            if (_plugInAssemblyList == null)
            {
                LoadPlugInAssemblies();
            }

            // check through all the plugin assemblies to detect the specified interface.
            foreach (Assembly assembly in _plugInAssemblyList)
            {
                foreach (Type namespaceType in assembly.GetTypes())
                {
                    if (namespaceType.Namespace != null && namespaceType.IsClass)
                    {
                        object pluginInstance = null;

                        foreach (Type intType in namespaceType.GetInterfaces())
                        {
                            switch (intType.Name)
                            {
                                case "IPluginGui":
                                    if (pluginInstance == null)
                                    {
                                        pluginInstance = Activator.CreateInstance(namespaceType);
                                    }

                                    if (pluginInstance != null)
                                    {
                                        _guiPlugins.Add(pluginInstance as IPluginGui);
                                    }
                                    break;

                                case "IPluginDataAccess":
                                    try
                                    {
                                        if (pluginInstance == null)
                                        {
                                            pluginInstance = Activator.CreateInstance(namespaceType);
                                        }

                                        if (pluginInstance != null)
                                        {
                                            _dataAccessPlugins.Add(pluginInstance as IPluginDataAccess);
                                        }
                                    }
                                    catch
                                    {
                                    }
                                    break;

                                case "IPluginImport":
                                    if (pluginInstance == null)
                                    {
                                        pluginInstance = Activator.CreateInstance(namespaceType);
                                    }

                                    if (pluginInstance != null)
                                    {
                                        _importPlugins.Add(pluginInstance as IPluginImport);
                                    }
                                    break;

                                case "IGraph":
                                    if (pluginInstance == null)
                                    {
                                        pluginInstance = Activator.CreateInstance(namespaceType);
                                    }

                                    if (pluginInstance != null)
                                    {
                                        _graphPlugins.Add(pluginInstance as IGraph);
                                    }
                                    break;
                            }
                        }
                    }
                }
            }

            // We need to initialise any plugin that uses the application configuration 
            if (_dataAccessPlugins.Count > 0)
            {
                foreach(IPluginDataAccess applicationConfigurationPlugin in _dataAccessPlugins)
                {
                    applicationConfigurationPlugin.SetDataAccessInterface(_dataAccessInterface);
                }
            }
        }

        public static List<IPluginGui> GuiPlugins
        {
            get { return PluginHost._guiPlugins; }
        }


        public static List<IPluginImport> ImportPlugins
        {
            get { return PluginHost._importPlugins; }
        }


        public static List<IPluginDataAccess> DataAccessPlugins
        {
            get { return PluginHost._dataAccessPlugins; }
        }

        public static List<IGraph> GraphPlugins
        {
            get { return PluginHost._graphPlugins; }
        }
    }
}
